#include <iostream>
#include <cmath>
#include <stdlib.h>
#include <time.h>
#include "main.h"


#include "WorldStatusUpdate.pb.h"
#include "Commands.pb.h"
#include "ProtobufUtil.hh"

using namespace strategy_protocol;
using namespace protoutil;
using namespace std;

int TheStrategy::distance(Position p, Position q)
{
    if(abs(p.x()-q.x())==0 || abs(p.y()-q.y()) == 0)
	{
        return max(abs(p.x()-q.x()),abs(p.y()-q.y()));
    }
	return min(abs(p.x()-q.x()),abs(p.y()-q.y()))+min(abs((p.x()-q.x())+(p.y()-q.y())),abs(p.y()-q.y()));
	//return max(abs(p.x()-q.x()), abs(p.y()-q.y()));
	//return min(abs(p.x()-q.x()),abs(p.y()-q.y()))+min(abs((p.x()-q.x())+(p.y()-q.y())),p.y()-q.y());
}

bool TheStrategy::same_pos(Position p, Position q)
{
	return (p.x()==q.x() && p.y()==q.y());
}

void TheStrategy::attackcmd(WorldStatusUpdateMessage_Soldier soldier, string enemy)
{
	CommandsMessage::Command* command = commands.add_commands();
	command->set_commandtype(CommandsMessage::ATTACK);
	CommandsMessage::Attack* attack = command->mutable_attackcommand();
	attack->set_withwhat(soldier.common().id());
	attack->set_what(enemy);
}

void TheStrategy::attackcmd(WorldStatusUpdateMessage_Worker worker, string enemy)
{
	CommandsMessage::Command* command = commands.add_commands();
	command->set_commandtype(CommandsMessage::ATTACK);
	CommandsMessage::Attack* attack = command->mutable_attackcommand();
	attack->set_withwhat(worker.common().id());
	attack->set_what(enemy);
}

void TheStrategy::movecmd(WorldStatusUpdateMessage_Soldier soldier, Position pos)
{
	CommandsMessage::Command* command = commands.add_commands();
	command->set_commandtype(CommandsMessage::MOVE);
	CommandsMessage::Move* move = command->mutable_movecommand();
	move->set_withwhat(soldier.common().id());
	move->mutable_toposition()->set_x(pos.x());
	move->mutable_toposition()->set_y(pos.y());
	cerr << "Soldier moves to (" <<pos.x()-translation_x<<","<<pos.y()-translation_y<<")"<<endl;
	cerr << "His target pos is (" <<soldiers_task[soldier.common().id()].target_pos.x()-translation_x <<","
					<<soldiers_task[soldier.common().id()].target_pos.y()-translation_y<<")"<<endl;
	mymap[pos.x()-translation_x][pos.y()-translation_y]='S';
	mymap[soldier.common().position().x()-translation_x][soldier.common().position().y()-translation_y]=' ';
}

void TheStrategy::movecmd(WorldStatusUpdateMessage_Worker worker, Position pos)
{
	CommandsMessage::Command* command = commands.add_commands();
	command->set_commandtype(CommandsMessage::MOVE);
	CommandsMessage::Move* move = command->mutable_movecommand();
	move->set_withwhat(worker.common().id());
	move->mutable_toposition()->set_x(pos.x());
	move->mutable_toposition()->set_y(pos.y());
	cerr << "Worker moves to (" <<pos.x()-translation_x<<","<<pos.y()-translation_y<<")"<<endl;
	mymap[pos.x()-translation_x][pos.y()-translation_y]='W';
	mymap[worker.common().position().x()-translation_x][worker.common().position().y()-translation_y]=' ';
	for(unsigned int i=0;i<mines.size();i++)
	{
		Position tmp = mines[i].common().position();
		if(same_pos(tmp,pos)) mymap[tmp.x()-translation_x][tmp.y()-translation_y]='M';
	}
}

void TheStrategy::traincmd(CommandsMessage::ActorType myman)
{
	CommandsMessage::Command* command = commands.add_commands();
	command->set_commandtype(CommandsMessage::TRAIN);
	CommandsMessage::Train* train = command->mutable_traincommand();
	train->set_withwhat(base_id);
	train->set_what(myman);
}

bool TheStrategy::reachable(Position p,Position q,bool considering_people)
{
	return (real_distance(p,q,considering_people)!=M*N);
}

bool TheStrategy::forbidden(char ch)
{
	return (ch=='X' || ch=='.' || ch=='B' || ch=='b' || ch=='-');
}

vector<Position> TheStrategy::exists_free_space(Position target_pos)
{
    vector<Position> free_spaces;
	if(boss_found)
	{
		for(int i=-soldier_shoot_dist;i<=soldier_shoot_dist;i++)
			if(mymap[target_pos.x()-translation_x+i][target_pos.y()-translation_y-soldier_shoot_dist]==' ' ||
               mymap[target_pos.x()-translation_x+i][target_pos.y()-translation_y-soldier_shoot_dist]=='.')
            {
           		Position pos=target_pos;
                pos.set_x(target_pos.x()+i);
                pos.set_y(target_pos.y()-soldier_shoot_dist);
                free_spaces.push_back(pos);
            }

		for(int i=-soldier_shoot_dist;i<=soldier_shoot_dist;i++)
			if(mymap[target_pos.x()-translation_x+i][target_pos.y()-translation_y+soldier_shoot_dist]==' ' ||
			   mymap[target_pos.x()-translation_x+i][target_pos.y()-translation_y+soldier_shoot_dist]=='.')
            {
             	Position pos=target_pos;
                pos.set_x(target_pos.x()+i);
                pos.set_y(target_pos.y()+soldier_shoot_dist);
                free_spaces.push_back(pos);
            }
        for(int j=-soldier_shoot_dist;j<=soldier_shoot_dist;j++)
			if(mymap[target_pos.x()-translation_x-soldier_shoot_dist][target_pos.y()-translation_y+j]==' ' ||
			   mymap[target_pos.x()-translation_x-soldier_shoot_dist][target_pos.y()-translation_y+j]=='.')
            {
             	Position pos=target_pos;
                pos.set_x(target_pos.x()-soldier_shoot_dist);
                pos.set_y(target_pos.y()+j);
                free_spaces.push_back(pos);
            }
        for(int j=-soldier_shoot_dist;j<=soldier_shoot_dist;j++)
			if(mymap[target_pos.x()-translation_x+soldier_shoot_dist][target_pos.y()-translation_y+j]==' ' ||
			   mymap[target_pos.x()-translation_x+soldier_shoot_dist][target_pos.y()-translation_y+j]=='.')
            {
             	Position pos=target_pos;
                pos.set_x(target_pos.x()+soldier_shoot_dist);
                pos.set_y(target_pos.y()+j);
                free_spaces.push_back(pos);
            }
	}
    return free_spaces;
}

void TheStrategy::initialize_tick(int i)
{
	srand((unsigned)time(0));

	soldier_has_target.clear();
	soldier_has_target.resize(world.soldiers_size(), false);
	worker_has_target.clear();
	worker_has_target.resize(world.workers_size(), false);

	commands.clear_commands();

	if(i==0)
	{
	    MYTEAM = world.myteam();
		base_id = world.bases(0).common().id();
		base_pos = world.bases(0).common().position();
		min_x = base_pos.x()-translation_x;
		min_y = base_pos.y()-translation_y;
		max_x = base_pos.x()-translation_x;
		max_y = base_pos.y()-translation_y;
		mymap.resize(M);
        for(int i=0;i<M;i++)
			mymap[i].resize(N,' ');
	}

	if(!boss_found && world.bases_size()>1)
	{
	    for(int i=0;i<2;i++)
        {
            if(world.bases(i).team()!=MYTEAM)
            {
                boss_id = world.bases(i).common().id();
                boss_pos = world.bases(i).common().position();
                boss_found = true;
            }
        }
	}
	cerr << "MyMap building ... \n";
	mymap_building();
	cerr << "DONE" << endl;
}
